package com.dataart.training.crimequalification.beans;

import com.dataart.training.crimequalification.domain.*;
import com.dataart.training.crimequalification.entity.*;
import com.dataart.training.crimequalification.matching.CrimeDetailsMatchingService;
import com.dataart.training.crimequalification.matching.MatchingResult;
import com.dataart.training.crimequalification.service.*;
import com.dataart.training.crimequalification.xmltools.XMLCrimeDetailsRepresentationService;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.util.Date;
import java.util.List;

@Component
@ManagedBean
@Scope("session")
public class CrimeComponentsBean {

    //-----------------------------------------------------------------

    private static final Logger logger = LoggerFactory
            .getLogger(CrimeComponentsBean.class);

    //----------------------------------------------------------------------

    @Autowired
    private CrimeObjectService crimeObjectService;

    @Autowired
    private CrimeSubjectService crimeSubjectService;

    @Autowired
    private XMLCrimeDetailsRepresentationService xmlRepresentationService;

    @Autowired
    private CrimeDetailsMatchingService matchingService;

    @Autowired
    private CrimeConsequenceService crimeConsequenceService;

    @Autowired
    private SubjectiveSideService subjectiveSideService;

    @Autowired
    private ObjectiveSideService objectiveSideService;

    @Autowired
    private QualificationTaskService qualificationTaskService;

    @Autowired
    private AuthenticationService authenticationService;

    @Autowired
    private UserService userService;

    //-----------------------------------------------------------------

    private CrimeDetails crimeDetails;

    // -------------------------- object components

    private MainCrimeObjectEntity mainCrimeObject;
    private SupplementaryCrimeObjectEntity supplementaryCrimeObject;
    private FacultativeCrimeObjectEntity facultativeCrimeObjectEntity;

    // -------------------------- consequences components

    private CrimeConsequenceEntity crimeConsequence;

    // -------------------------- subjective side components

    private MotiveEntity motive;
    private IntentEntity intent;
    private IndiscretionEntity indiscretion;
    private PurposeEntity purpose;
    private DelinquentStateEntity delinquentState;

    // -------------------------- auxiliary ajax flags

    private boolean withFacultativeCrimeObject;
    private boolean withSupplementaryCrimeObject;

    private boolean withMotive;
    private boolean withPurpose;
    private boolean withDelinquentState;
    private boolean withIntent;
    private boolean withIndiscretion;

    private boolean guiltTypeChosen;

    // -------------------------- subject components

    private CrimeSubjectEntity crimeSubject;

    // -------------------------- object possibilities

    private List<MainCrimeObjectEntity> possibleMainCrimeObjects;
    private List<FacultativeCrimeObjectEntity> possibleFacultativeCrimeObjects;
    private List<SupplementaryCrimeObjectEntity> possibleSupplementaryCrimeObjects;

    //--------------------------- objective side possibilities

    private List<CriminalActEntity> possibleCriminalActs;
    private List<ActFeatureEntity> possibleActFeatures;

    // -------------------------- subject possibilities

    private List<CrimeSubjectEntity> possibleCrimeSubjects;

    // -------------------------- consequence possibilities

    private List<CrimeConsequenceEntity> possibleCrimeConsequences;

    // -------------------------- subjective side possibilities

    private List<IntentEntity> possibleIntents;
    private List<IndiscretionEntity> possibleIndiscretions;

    private List<MotiveEntity> possibleMotives;
    private List<PurposeEntity> possiblePurposes;
    private List<DelinquentStateEntity> possibleStates;

    //-----------------------------------------------------------------

    // -------------------------- codenames

    private String mainCrimeObjectCodeName;
    private String facultativeObjectCodeName;
    private String supplementaryObjectCodeName;

    private String crimeSubjectCodename;

    private String crimeConsequenceCodename;

    private String intentCodename;
    private String indiscretionCodename;
    private String motiveCodename;
    private String purposeCodename;
    private String delinquentStateCodename;

    private String criminalActCodename;
    private String actFeatureCodename;

    //-----------------------------------------------------------------

    private QualificationTaskEntity task;
    private boolean taskChosen;

    private boolean solutionSubmitted;
    private MatchingResult matchingResult;
    private List<String> verboseResult;
    private List<String> submittedDetailsInfo;
    private String rightPercentage;

    //-----------------------------------------------------------------

    /**
     * @throws IOException
     */
    public void noTaskChosenRedirect() throws IOException {

        ConfigurationUtils cu = new ConfigurationUtils();

        HttpServletRequest request
                = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();

        if (!cu.isTaskChosen(FacesContext.getCurrentInstance())) {

            String taskId = request.getParameter("taskId");

            if (taskId != null) {
                task = qualificationTaskService.getTaskById(Integer.parseInt(taskId));
                cu.setTaskChosen(task != null, FacesContext.getCurrentInstance());

                if (task == null) {
                    FacesContext.getCurrentInstance().getExternalContext().redirect(request.getContextPath()
                            + "/views/choosetask.xhtml");
                }
            } else {
                FacesContext.getCurrentInstance().getExternalContext().redirect(request.getContextPath()
                        + "/views/choosetask.xhtml");
            }
        }
    }

    public void resetLocalValues() {

        ConfigurationUtils cu = new ConfigurationUtils();

        cu.setTaskChosen(false, FacesContext.getCurrentInstance());

        solutionSubmitted = false;

        crimeDetails = new CrimeDetails();

        mainCrimeObject = null;
        supplementaryCrimeObject = null;
        facultativeCrimeObjectEntity = null;
        crimeConsequence = null;

        motive = null;
        intent = null;
        indiscretion = null;
        purpose = null;
        delinquentState = null;


        mainCrimeObjectCodeName = null;
        facultativeObjectCodeName = null;
        supplementaryObjectCodeName = null;
        crimeSubjectCodename = null;
        crimeConsequenceCodename = null;
        intentCodename = null;
        indiscretionCodename = null;
        motiveCodename = null;
        purposeCodename = null;
        delinquentStateCodename = null;
        criminalActCodename = null;
        actFeatureCodename = null;

        withFacultativeCrimeObject = false;
        withSupplementaryCrimeObject = false;
        withPurpose = false;
        withMotive = false;
        withDelinquentState = false;
        withIntent = false;
        withIndiscretion = false;

        guiltTypeChosen = false;

        possibleMainCrimeObjects = crimeObjectService.getAllMainCrimeObjects();
        possibleFacultativeCrimeObjects = crimeObjectService.getAllFacultativeCrimeObjects();
        possibleSupplementaryCrimeObjects = crimeObjectService.getAllSupplementaryCrimeObjects();

        possibleCrimeSubjects = crimeSubjectService.getAllCrimeSubjects();

        possibleCrimeConsequences = crimeConsequenceService.getAllCrimeConsequences();

        possibleIntents = subjectiveSideService.getAllIntents();
        possibleMotives = subjectiveSideService.getAllMotives();
        possibleIndiscretions = subjectiveSideService.getAllIndiscretionTypes();
        possiblePurposes = subjectiveSideService.getAllPurposes();
        possibleStates = subjectiveSideService.getAllDelinquentStates();

        possibleCriminalActs = objectiveSideService.getAllCriminalActs();
        possibleActFeatures = objectiveSideService.getAllActFeatures();

    }

    /**
     * @throws IOException
     */
    @PostConstruct
    public void init() throws IOException {

        resetLocalValues();
        noTaskChosenRedirect();

    }

    public void addFacultativeCrimeObject(AjaxBehaviorEvent event) {
        withFacultativeCrimeObject = true;
    }

    public String reset() throws IOException {
        init();
        return "/views/crimequalificator?faces-redirect=true";
    }

    public void addSupplementaryCrimeObject(AjaxBehaviorEvent event) {
        withSupplementaryCrimeObject = true;
    }

    public void addMotive(AjaxBehaviorEvent event) {
        withMotive = true;
    }

    public void addPurpose(AjaxBehaviorEvent event) {
        withPurpose = true;
    }

    public void addDelinquentStatr(AjaxBehaviorEvent event) {
        withDelinquentState = true;
    }

    public void chooseGuiltTypeIntent(AjaxBehaviorEvent event) {
        guiltTypeChosen = true;
        withIntent = true;
    }

    public void chooseGuiltTypeIndiscretion(AjaxBehaviorEvent event) {
        guiltTypeChosen = true;
        withIndiscretion = true;
    }

    private List<String> splitString(String stringToSplit) {
        return Lists.newArrayList(Splitter.on("\n").split(stringToSplit));
    }

    public boolean decisionIsCorrect(String decision) {
        return !(decision.toLowerCase().contains("неверно"));
    }

    /**
     *
     */
    public void submitCrimeDetails() {

        // always present
        mainCrimeObject = crimeObjectService.getMainCrimeObjectByCodename(mainCrimeObjectCodeName);

        if (withFacultativeCrimeObject) {
            facultativeCrimeObjectEntity = crimeObjectService.
                    getFacultativeCrimeObjectByCodename(facultativeObjectCodeName);
        }

        if (withSupplementaryCrimeObject) {
            supplementaryCrimeObject = crimeObjectService.
                    getSupplementaryCrimeObjectByCodename(supplementaryObjectCodeName);
        }

        // always present
        crimeSubject = crimeSubjectService.getCrimeSubjectByCodename(crimeSubjectCodename);
        crimeConsequence = crimeConsequenceService.getCrimeConsequenceByCodeName(crimeConsequenceCodename);

        // subjective side

        CrimeObject crimeObject = new CrimeObject();
        crimeObject.setCrimeObjectEntity(mainCrimeObject);
        if (withFacultativeCrimeObject) {
            crimeObject.setFacultativeCrimeObjectEntity(facultativeCrimeObjectEntity);
            crimeObject.setWithFacultativeObject(true);
        } else {
            crimeObject.setWithFacultativeObject(false);
        }

        if (withSupplementaryCrimeObject) {
            crimeObject.setSupplementaryCrimeObjectEntity(supplementaryCrimeObject);
            crimeObject.setWithSupplementaryObject(true);
        } else {
            crimeObject.setWithSupplementaryObject(false);
        }

        crimeDetails.setCrimeObject(crimeObject);

        crimeDetails.setCrimeSubject(crimeSubject);

        crimeDetails.setCrimeConsequence(crimeConsequence);

        if (withIntent) {
            indiscretion = null;
            intent = subjectiveSideService.getIntentByCodename(intentCodename);
        }

        if (withIndiscretion) {
            intent = null;
            indiscretion = subjectiveSideService.getIndiscretionByCodename(indiscretionCodename);

        }

        Guilt guilt = new Guilt(intent, indiscretion);

        if (withMotive) {
            motive = subjectiveSideService.getMotiveByCodename(motiveCodename);
        }

        if (withPurpose) {
            purpose = subjectiveSideService.getPurposeByCodename(purposeCodename);
        }

        if (withDelinquentState) {
            delinquentState = subjectiveSideService.getDelinquentStateByCodename(delinquentStateCodename);
        }

        SubjectiveSide subjectiveSide = new SubjectiveSide(guilt, motive, purpose, delinquentState);

        crimeDetails.setSubjectiveSide(subjectiveSide);

        logger.debug(crimeDetails.toString());

        // Building XML

        try {

            String pathToCreatedXML = xmlRepresentationService.crimeDetailsToXML(crimeDetails);
            CrimeDetails parsedDetails = xmlRepresentationService.parseXMLForCrimeDetails(pathToCreatedXML);
            logger.debug(parsedDetails.toString());

            CrimeDetails solution = xmlRepresentationService.getTaskSolution(task.getTaskId());
            logger.debug(solution.toString());

            matchingResult = matchingService.matchWholeCrimeDetails(parsedDetails, solution);
            logger.debug(String.valueOf(matchingResult.isMatches() + matchingResult.getVerboseResult()));

            verboseResult = splitString(matchingResult.getVerboseResult());
            submittedDetailsInfo = splitString(crimeDetails.getCrimeDetailsInfo());

            TaskSolutionResultEntity taskSolutionResult = new TaskSolutionResultEntity();
            if (matchingResult.isMatches()) {
                taskSolutionResult.setAbsoluteResult(1);
            } else {
                taskSolutionResult.setAbsoluteResult(0);
            }

            Double percentage = (Double.valueOf(matchingResult.getPointsGained()) /
                    Double.valueOf(matchingResult.getTotalPoints())) * 100;

            rightPercentage = String.valueOf(percentage.intValue());

            taskSolutionResult.setSubmissionDate(new Date());
            taskSolutionResult.setTask(task);
            taskSolutionResult.setUser(authenticationService.getCurrentLoggedInUser(userService));
            taskSolutionResult.setUserSolutionFilename(pathToCreatedXML);
            taskSolutionResult.setVerboseResult(matchingResult.getVerboseResult());
            taskSolutionResult.setPercentage(percentage.intValue());

            qualificationTaskService.saveUserSubmittedTaskSolutionResult(taskSolutionResult);

            solutionSubmitted = true;

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }

//        logger.debug(matchingService.matchWholeCrimeDetails(parsedDetails,
//                xmlRepresentationService.retrieveAllCodexEntries().get(0)).getVerboseResult());

    }

    //-----------------------------------------------------------------

    public FacultativeCrimeObjectEntity getFacultativeCrimeObjectEntity() {
        return facultativeCrimeObjectEntity;
    }

    public void setFacultativeCrimeObjectEntity(FacultativeCrimeObjectEntity facultativeCrimeObjectEntity) {
        this.facultativeCrimeObjectEntity = facultativeCrimeObjectEntity;
    }

    public SupplementaryCrimeObjectEntity getSupplementaryCrimeObject() {
        return supplementaryCrimeObject;
    }

    public void setSupplementaryCrimeObject(SupplementaryCrimeObjectEntity supplementaryCrimeObject) {
        this.supplementaryCrimeObject = supplementaryCrimeObject;
    }

    public MainCrimeObjectEntity getMainCrimeObject() {
        return mainCrimeObject;
    }

    public void setMainCrimeObject(MainCrimeObjectEntity mainCrimeObject) {
        this.mainCrimeObject = mainCrimeObject;
    }

    public boolean isWithFacultativeCrimeObject() {
        return withFacultativeCrimeObject;
    }

    public void setWithFacultativeCrimeObject(boolean withFacultativeCrimeObject) {
        this.withFacultativeCrimeObject = withFacultativeCrimeObject;
    }

    public List<MainCrimeObjectEntity> getPossibleMainCrimeObjects() {
        return possibleMainCrimeObjects;
    }

    public void setPossibleMainCrimeObjects(List<MainCrimeObjectEntity> possibleMainCrimeObjects) {
        this.possibleMainCrimeObjects = possibleMainCrimeObjects;
    }

    public String getMainCrimeObjectCodeName() {
        return mainCrimeObjectCodeName;
    }

    public void setMainCrimeObjectCodeName(String mainCrimeObjectCodeName) {
        this.mainCrimeObjectCodeName = mainCrimeObjectCodeName;
    }

    public List<FacultativeCrimeObjectEntity> getPossibleFacultativeCrimeObjects() {
        return possibleFacultativeCrimeObjects;
    }

    public void setPossibleFacultativeCrimeObjects(List<FacultativeCrimeObjectEntity> possibleFacultativeCrimeObjects) {
        this.possibleFacultativeCrimeObjects = possibleFacultativeCrimeObjects;
    }

    public String getFacultativeObjectCodeName() {
        return facultativeObjectCodeName;
    }

    public void setFacultativeObjectCodeName(String facultativeObjectCodeName) {
        this.facultativeObjectCodeName = facultativeObjectCodeName;
    }

    public String getSupplementaryObjectCodeName() {
        return supplementaryObjectCodeName;
    }

    public void setSupplementaryObjectCodeName(String supplementaryObjectCodeName) {
        this.supplementaryObjectCodeName = supplementaryObjectCodeName;
    }

    public boolean isWithSupplementaryCrimeObject() {
        return withSupplementaryCrimeObject;
    }

    public void setWithSupplementaryCrimeObject(boolean withSupplementaryCrimeObject) {
        this.withSupplementaryCrimeObject = withSupplementaryCrimeObject;
    }

    public List<SupplementaryCrimeObjectEntity> getPossibleSupplementaryCrimeObjects() {
        return possibleSupplementaryCrimeObjects;
    }

    public void setPossibleSupplementaryCrimeObjects(List<SupplementaryCrimeObjectEntity> possibleSupplementaryCrimeObjects) {
        this.possibleSupplementaryCrimeObjects = possibleSupplementaryCrimeObjects;
    }

    public List<CrimeSubjectEntity> getPossibleCrimeSubjects() {
        return possibleCrimeSubjects;
    }

    public void setPossibleCrimeSubjects(List<CrimeSubjectEntity> possibleCrimeSubjects) {
        this.possibleCrimeSubjects = possibleCrimeSubjects;
    }

    public CrimeSubjectEntity getCrimeSubject() {
        return crimeSubject;
    }

    public void setCrimeSubject(CrimeSubjectEntity crimeSubject) {
        this.crimeSubject = crimeSubject;
    }

    public String getCrimeSubjectCodename() {
        return crimeSubjectCodename;
    }

    public void setCrimeSubjectCodename(String crimeSubjectCodename) {
        this.crimeSubjectCodename = crimeSubjectCodename;
    }

    public List<CrimeConsequenceEntity> getPossibleCrimeConsequences() {
        return possibleCrimeConsequences;
    }

    public void setPossibleCrimeConsequences(List<CrimeConsequenceEntity> possibleCrimeConsequences) {
        this.possibleCrimeConsequences = possibleCrimeConsequences;
    }

    public void setCrimeConsequenceCodename(String crimeConsequenceCodename) {
        this.crimeConsequenceCodename = crimeConsequenceCodename;
    }

    public String getCrimeConsequenceCodename() {
        return crimeConsequenceCodename;
    }

    public List<IntentEntity> getPossibleIntents() {
        return possibleIntents;
    }

    public void setPossibleIntents(List<IntentEntity> possibleIntents) {
        this.possibleIntents = possibleIntents;
    }

    public List<IndiscretionEntity> getPossibleIndiscretions() {
        return possibleIndiscretions;
    }

    public void setPossibleIndiscretions(List<IndiscretionEntity> possibleIndiscretions) {
        this.possibleIndiscretions = possibleIndiscretions;
    }

    public List<MotiveEntity> getPossibleMotives() {
        return possibleMotives;
    }

    public void setPossibleMotives(List<MotiveEntity> possibleMotives) {
        this.possibleMotives = possibleMotives;
    }

    public List<PurposeEntity> getPossiblePurposes() {
        return possiblePurposes;
    }

    public void setPossiblePurposes(List<PurposeEntity> possiblePurposes) {
        this.possiblePurposes = possiblePurposes;
    }

    public List<DelinquentStateEntity> getPossibleStates() {
        return possibleStates;
    }

    public void setPossibleStates(List<DelinquentStateEntity> possibleStates) {
        this.possibleStates = possibleStates;
    }

    public MotiveEntity getMotive() {
        return motive;
    }

    public void setMotive(MotiveEntity motive) {
        this.motive = motive;
    }

    public boolean isWithMotive() {
        return withMotive;
    }

    public void setWithMotive(boolean withMotive) {
        this.withMotive = withMotive;
    }

    public boolean isWithPurpose() {
        return withPurpose;
    }

    public void setWithPurpose(boolean withPurpose) {
        this.withPurpose = withPurpose;
    }

    public boolean isWithDelinquentState() {
        return withDelinquentState;
    }

    public void setWithDelinquentState(boolean withDelinquentState) {
        this.withDelinquentState = withDelinquentState;
    }

    public IntentEntity getIntent() {
        return intent;
    }

    public void setIntent(IntentEntity intent) {
        this.intent = intent;
    }

    public IndiscretionEntity getIndiscretion() {
        return indiscretion;
    }

    public void setIndiscretion(IndiscretionEntity indiscretion) {
        this.indiscretion = indiscretion;
    }

    public PurposeEntity getPurpose() {
        return purpose;
    }

    public void setPurpose(PurposeEntity purpose) {
        this.purpose = purpose;
    }

    public DelinquentStateEntity getDelinquentState() {
        return delinquentState;
    }

    public void setDelinquentState(DelinquentStateEntity delinquentState) {
        this.delinquentState = delinquentState;
    }

    public String getIntentCodename() {
        return intentCodename;
    }

    public void setIntentCodename(String intentCodename) {
        this.intentCodename = intentCodename;
    }

    public String getIndiscretionCodename() {
        return indiscretionCodename;
    }

    public void setIndiscretionCodename(String indiscretionCodename) {
        this.indiscretionCodename = indiscretionCodename;
    }

    public String getMotiveCodename() {
        return motiveCodename;
    }

    public void setMotiveCodename(String motiveCodename) {
        this.motiveCodename = motiveCodename;
    }

    public String getPurposeCodename() {
        return purposeCodename;
    }

    public void setPurposeCodename(String purposeCodename) {
        this.purposeCodename = purposeCodename;
    }

    public String getDelinquentStateCodename() {
        return delinquentStateCodename;
    }

    public void setDelinquentStateCodename(String delinquentStateCodename) {
        this.delinquentStateCodename = delinquentStateCodename;
    }

    public boolean isWithIntent() {
        return withIntent;
    }

    public void setWithIntent(boolean withIntent) {
        this.withIntent = withIntent;
    }

    public boolean isWithIndiscretion() {
        return withIndiscretion;
    }

    public void setWithIndiscretion(boolean withIndiscretion) {
        this.withIndiscretion = withIndiscretion;
    }

    public boolean isGuiltTypeChosen() {
        return guiltTypeChosen;
    }

    public void setGuiltTypeChosen(boolean guiltTypeChosen) {
        this.guiltTypeChosen = guiltTypeChosen;
    }

    public QualificationTaskEntity getTask() {
        return task;
    }

    public void setTask(QualificationTaskEntity task) {
        this.task = task;
    }

    public boolean isTaskChosen() {
        return taskChosen;
    }

    public void setTaskChosen(boolean taskChosen) {
        this.taskChosen = taskChosen;
    }

    public CrimeDetails getCrimeDetails() {
        return crimeDetails;
    }

    public void setCrimeDetails(CrimeDetails crimeDetails) {
        this.crimeDetails = crimeDetails;
    }

    public boolean isSolutionSubmitted() {
        return solutionSubmitted;
    }

    public void setSolutionSubmitted(boolean solutionSubmitted) {
        this.solutionSubmitted = solutionSubmitted;
    }

    public MatchingResult getMatchingResult() {
        return matchingResult;
    }

    public void setMatchingResult(MatchingResult matchingResult) {
        this.matchingResult = matchingResult;
    }

    public List<String> getVerboseResult() {
        return verboseResult;
    }

    public void setVerboseResult(List<String> verboseResult) {
        this.verboseResult = verboseResult;
    }

    public List<String> getSubmittedDetailsInfo() {
        return submittedDetailsInfo;
    }

    public void setSubmittedDetailsInfo(List<String> submittedDetailsInfo) {
        this.submittedDetailsInfo = submittedDetailsInfo;
    }

    public String getRightPercentage() {
        return rightPercentage;
    }

    public void setRightPercentage(String rightPercentage) {
        this.rightPercentage = rightPercentage;
    }

    public List<CriminalActEntity> getPossibleCriminalActs() {
        return possibleCriminalActs;
    }

    public void setPossibleCriminalActs(List<CriminalActEntity> possibleCriminalActs) {
        this.possibleCriminalActs = possibleCriminalActs;
    }

    public List<ActFeatureEntity> getPossibleActFeatures() {
        return possibleActFeatures;
    }

    public void setPossibleActFeatures(List<ActFeatureEntity> possibleActFeatures) {
        this.possibleActFeatures = possibleActFeatures;
    }

    public String getCriminalActCodename() {
        return criminalActCodename;
    }

    public void setCriminalActCodename(String criminalActCodename) {
        this.criminalActCodename = criminalActCodename;
    }

    public String getActFeatureCodename() {
        return actFeatureCodename;
    }

    public void setActFeatureCodename(String actFeatureCodename) {
        this.actFeatureCodename = actFeatureCodename;
    }

    //-----------------------------------------------------------------

}
